/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2025. All rights reserved.
 */

import { hvigorCore } from '@ohos/hvigor';

import { findNodeEntryFiles } from '@ohos/hvigor-arkts-compose';
import fs from 'fs';
import path from 'path';
import { PackageJson } from 'type-fest';

import { parsingProfileName } from '../../common/common-util.js';
import { BuildArtifactConst, BuildDirConst } from '../../const/build-directory-const.js';

import { CommonConst, DefaultTargetConst } from '../../const/common-const.js';
import { ModuleType } from '../../enum/module-type-enum.js';
import { CoreModuleModelImpl } from '../../model/module/core-module-model-impl.js';
import { LegacyModuleModelImpl } from '../../model/module/legacy-module-model-impl.js';
import { ModuleModel } from '../../model/module/module-model.js';
import { CoreProjectModelImpl } from '../../model/project/core-project-model-impl.js';
import { ProjectModel } from '../../model/project/project-model.js';
import { ConfigJson } from '../../options/configure/config-json-options.js';
import { ModuleJson } from '../../options/configure/module-json-options.js';
import { RouterMapOptions } from '../../options/configure/router-map-options.js';
import { ArtifactType } from '../../tasks/har/har-extend-info.js';
import { OhPackageJsonOpt } from '../../tasks/task/ohos-har-task.js';
import { ErrorUrl } from '../../utils/error-url.js';
import { ohPackageLoader } from '../../utils/loader/file/oh-package-loader.js';
import { resModelLoader } from '../../utils/loader/file/res-model-loader.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { BasePackageResolver } from '../../utils/resolver/base-package-resolver.js';
import { NpmPackageResolver } from '../../utils/resolver/npm-package-resolver.js';
import { OhpmPackageResolver } from '../../utils/resolver/ohpm-package-resolver.js';
import { DefaultModuleDependency } from './core/default-module-dependency.js';

import { DependencyBuilder } from './core/dependency-builder.js';

import { compareDependencyEnum, Dependency, DependencyEnum, DependencyType } from './core/dependency-interface.js';
import { ModuleDependencyInfo } from './module-dependency-info.js';
import ConfigOptObj = ConfigJson.ConfigOptObj;
import ModuleOptObj = ModuleJson.ModuleOptObj;
import RouterMapObj = RouterMapOptions.RouterMapObj;
import { LegacyDependencyManagerHelper } from './legacy-dependency-manager-helper.js';
import { getJson5Obj, OhosPackageJson } from '../../utils/dependency-util.js';
import { toUnixPath } from '../../utils/copy-resources-util.js';
import { FileUtil } from '../../utils/file-util.js';
import { ohModulesLockLoader } from '../../utils/loader/file/oh-modules-lock-loader.js';

const log = OhosLogger.getLogger('ohos-dependency-manager');

const solution =
  'The OpenHarmony npm package or module is not compatible with the current model. ' +
  'Use a compatible OpenHarmony npm package or module. ' +
  `For details about the stage and FA models, see ${ErrorUrl.ERROR_URL.modelDiff}.`;

export type DependencyCollector = (node: ModulePkgNode) => boolean;
export type DependencyFilter = (node: ModulePkgNode, pre: boolean, post: boolean) => boolean;

/**
 * 管理har的依赖，提供获取当前模块依赖的数据信息
 *
 * @since 2022/5/7
 */
export class DependencyManager {
  private _allDependency: Set<string> = new Set<string>();
  private readonly _model: ModuleModel | ProjectModel;
  private readonly _projectModel: ProjectModel | undefined;
  private readonly _moduleName: string;
  private readonly _isFaMode: boolean;
  private readonly _isOhpmDependency: boolean;
  private legacyDependencyManagerHelper: LegacyDependencyManagerHelper;

  constructor(isFaMode: boolean, model: ModuleModel | ProjectModel, project?: ProjectModel) {
    this._model = model;
    this._projectModel = project;
    this._moduleName = model.getName();
    this._isFaMode = isFaMode;
    this._isOhpmDependency = this.isOhpmDependency();
    this.legacyDependencyManagerHelper = new LegacyDependencyManagerHelper(model.getName());
  }

  private isOhpmDependency(): boolean {
    if (this._projectModel) {
      return this._projectModel.isOhpmProject();
    }
    return this._model.isOhpmProject();
  }

  createModelDependencyInfo(): ModuleDependencyInfo {
    // 不收集devDependencies(前置pre), 收集hsp模块, 但是不收集hsp模块的子依赖(后置post)
    const filter = (node: ModulePkgNode, pre: boolean, post: boolean): boolean =>
      (pre && node.npm.getDependencyEnum() !== DependencyEnum.DEV_DEPENDENCIES) ||
      (post && node.npm.getDependencyType() !== DependencyType.DEPENDENCY_TYPE_HSP);
    const packagePath: string = this._isOhpmDependency ? this._model.getOhPackageJson5Path() : this._model.getPackageJsonPath();
    const { npmDependencies, moduleDependencyMap, modulePkgNode, projectPkgNode } = this.getDependenciesByFilter(this._model, packagePath, filter, filter);
    log.debug(`Module ${this._moduleName} Collected Dependency: ` + `${npmDependencies.map((value) => value.getDependencyRootPath())}`);
    log.debug(`Module ${this._moduleName}'s total dependency: ${npmDependencies.length}`);
    return new ModuleDependencyInfo(modulePkgNode.npm, modulePkgNode, projectPkgNode, moduleDependencyMap, npmDependencies);
  }

  // 收集所有依赖，包括Hsp的间接依赖以及Dev的依赖
  collectModelOrProjectAllDependencyInfo() {
    const filter = (node: ModulePkgNode, pre: boolean, post: boolean): boolean => true;
    const packagePath: string = this._isOhpmDependency ? this._model.getOhPackageJson5Path() : this._model.getPackageJsonPath();
    const { npmDependencies } = this.getDependenciesByFilter(this._model, packagePath, filter, filter);
    return npmDependencies;
  }

  /**
   * 构造本地模块为Dependency对象
   *
   * @param model
   * @param modulePkgPath
   */
  buildSelfDependency(model: ProjectModel | ModuleModel, modulePkgPath: string) {
    const isProject = model instanceof CoreProjectModelImpl;
    const hvigorNode = isProject ? (model as ProjectModel).getProject() : (model as ModuleModel).getModule();
    const pkgObj = this.getFinalOhPkgJsonObj(modulePkgPath);

    const moduleType = this.getHarmonyDependencyType(modulePkgPath, pkgObj, modulePkgPath, hvigorNode.getName());
    let moduleJsonPath: string | undefined;
    if (!isProject) {
      moduleJsonPath = [path.resolve(model.getProjectDir(), 'src/main/module.json5'), path.resolve(model.getProjectDir(), 'src/main/module.json')].find(
        (json) => fs.existsSync(json),
      );
    }
    const builder = new DependencyBuilder()
      .setDependencyName(hvigorNode.getName())
      .setDependencyRootPath(hvigorNode.getNodeDir())
      .setPkgJsonPath(modulePkgPath)
      .setPkgJsonObj(pkgObj)
      .setIsLocal(true)
      .setModule(hvigorNode)
      .setModuleJsonObj(this.getModuleJsonObj(moduleJsonPath))
      .setIsModuleDependency(!isProject)
      .setDependencyEnum(DependencyEnum.DEPENDENCIES);
    if (moduleType) {
      builder.setDependencyType(moduleType);
    }

    return builder.buildDefaultModuleDependency();
  }

  // 收集所有依赖
  collectAllDependencies() {
    const packagePath: string = this._isOhpmDependency ? this._model.getOhPackageJson5Path() : this._model.getPackageJsonPath();
    // 过滤工程节点dev依赖
    const filter = (node: ModulePkgNode, pre: boolean, post: boolean): boolean => post || node.npm.getDependencyEnum() !== DependencyEnum.DEV_DEPENDENCIES;
    return this.getDependenciesByFilter(this._model, packagePath, undefined, filter);
  }

  // 语境信息表需要增加额外收集信息
  // 收集所有依赖包括直接依赖的devDependencies和间接依赖的dependencies中的相关信息
  collectAllDependenciesForPkgInfo() {
    const packagePath: string = this._isOhpmDependency ? this._model.getOhPackageJson5Path() : this._model.getPackageJsonPath();

    // 排除hsp的间接依赖
    const filter = (node: ModulePkgNode, pre: boolean): boolean => pre || node.npm?.getDependencyType() !== DependencyType.DEPENDENCY_TYPE_HSP;
    return this.getDependenciesForPkgInfo(this._model, packagePath, filter);
  }

  getDependenciesByFilter(model: ProjectModel | ModuleModel, pkgPath: string, filter?: DependencyFilter, projectFilter?: DependencyFilter) {
    this._allDependency.clear();
    const npmDependencies: Dependency[] = [];
    const moduleDependencyMap: Map<string, Dependency> = new Map<string, Dependency>();
    const self = this.buildSelfDependency(model, pkgPath);
    const modulePkgNode: ModulePkgNode = {
      moduleName: this._moduleName,
      modulePkgJsonPath: pkgPath,
      children: [],
      npm: self,
      module: self,
    };
    this.collectHarDependency(modulePkgNode);
    this.collectFromTree(modulePkgNode, npmDependencies, moduleDependencyMap, filter);
    const projectPkgNode = this.collectProjectHarDependencyForHap(moduleDependencyMap, npmDependencies, projectFilter);
    return { npmDependencies, moduleDependencyMap, modulePkgNode, projectPkgNode };
  }

  // har/hsp额外收集工程级别的源码依赖，当前该方法仅提供给语境信息表使用
  private getDependenciesForPkgInfo(model: ProjectModel | ModuleModel, pkgPath: string, filter?: DependencyFilter, projectFilter?: DependencyFilter) {
    this._allDependency.clear();
    const npmDependencies: Dependency[] = [];
    const moduleDependencyMap: Map<string, Dependency> = new Map<string, Dependency>();
    const self = this.buildSelfDependency(model, pkgPath);
    const modulePkgNode: ModulePkgNode = {
      moduleName: this._moduleName,
      modulePkgJsonPath: pkgPath,
      children: [],
      npm: self,
      module: self,
    };
    this.collectHarDependency(modulePkgNode);
    this.collectFromTree(modulePkgNode, npmDependencies, moduleDependencyMap, filter);
    const projectPkgNode = this.collectModuleHarDependencyForHap(moduleDependencyMap, npmDependencies, projectFilter);
    return { npmDependencies, moduleDependencyMap, modulePkgNode, projectPkgNode };
  }

  private collectModuleHarDependencyForHap(moduleDependencyMap: Map<string, Dependency>, npmDependencies: Dependency[], filter?: DependencyFilter) {
    if (!this._projectModel) {
      return;
    }
    const packagePath: string = this._isOhpmDependency ? this._projectModel.getOhPackageJson5Path() : this._projectModel.getPackageJsonPath();
    const self = this.buildSelfDependency(this._projectModel, packagePath);
    const modulePkgObj: ModulePkgNode = {
      moduleName: this._moduleName,
      modulePkgJsonPath: packagePath,
      children: [],
      npm: self,
      module: self,
    };
    this.collectHarDependency(modulePkgObj);
    const projectFilter = (node: ModulePkgNode, pre: boolean, post: boolean) => {
      return !filter || filter(node, pre, post);
    };
    this.collectFromTree(modulePkgObj, npmDependencies, moduleDependencyMap, projectFilter);
    return modulePkgObj;
  }

  // 收集当前模块下的所有hsp依赖
  collectAllHspDependencies() {
    const { moduleDependencyMap } = this.collectAllDependencies();
    return [...moduleDependencyMap.entries()].filter(([, depend]) => depend.getDependencyType() === DependencyType.DEPENDENCY_TYPE_HSP);
  }

  private collectProjectHarDependencyForHap(moduleDependencyMap: Map<string, Dependency>, npmDependencies: Dependency[], filter?: DependencyFilter) {
    if (!this._projectModel) {
      return;
    }
    const packagePath: string = this._isOhpmDependency ? this._projectModel.getOhPackageJson5Path() : this._projectModel.getPackageJsonPath();
    const self = this.buildSelfDependency(this._projectModel, packagePath);
    const modulePkgObj: ModulePkgNode = {
      moduleName: this._moduleName,
      modulePkgJsonPath: packagePath,
      children: [],
      npm: self,
      module: self,
    };
    this.collectHarDependency(modulePkgObj);
    const projectFilter = (node: ModulePkgNode, pre: boolean, post: boolean) => {
      // 在传入filter基础上, 增加过滤条件, 只在当前模块为hap时, 去收集项目下配置的本地模块依赖
      if (node.module && !(this._model as ModuleModel).isHapModule()) {
        return false;
      }
      return !filter || filter(node, pre, post);
    };
    this.collectFromTree(modulePkgObj, npmDependencies, moduleDependencyMap, projectFilter);
    return modulePkgObj;
  }

  /**
   *
   * @param modulePkgObj
   * @private
   */
  private collectHarDependency(modulePkgObj: ModulePkgNode) {
    const pkgQueue: ModulePkgNode[] = [modulePkgObj];
    while (pkgQueue.length > 0) {
      // queue中存放依赖中package.json的路径和模块名称
      let curModulePkgObj = pkgQueue.shift();
      if (curModulePkgObj === undefined || !fs.existsSync(curModulePkgObj.modulePkgJsonPath)) {
        continue;
      }
      const calculatedPkgJsonPath = ohPackageLoader.getNodeOhPackagePath(curModulePkgObj.modulePkgJsonPath);

      const curPkgObj = this.getFinalOhPkgJsonObj(calculatedPkgJsonPath);
      if (curPkgObj === null) {
        continue;
      }
      this.resolveDeps(curModulePkgObj, pkgQueue, DependencyEnum.DEPENDENCIES, curPkgObj?.dependencies);
      this.resolveDeps(curModulePkgObj, pkgQueue, DependencyEnum.DYNAMIC_DEPENDENCIES, curPkgObj?.dynamicDependencies);
      this.resolveDeps(curModulePkgObj, pkgQueue, DependencyEnum.DEV_DEPENDENCIES, curPkgObj?.devDependencies);

      const cacheKey = `${curModulePkgObj.npm.getDependencyName()}@${curModulePkgObj.modulePkgJsonPath}`;
      let depNode = hvigorCore.getCache('DEP_NODE');
      !depNode.has(cacheKey) && depNode.set(cacheKey, curModulePkgObj);
    }
  }

  private resolveDeps(modulePkgNode: ModulePkgNode, queue: ModulePkgNode[], depType: DependencyEnum, dependencies: any) {
    for (const pkgName in dependencies ?? {}) {
      this._allDependency.add(`${modulePkgNode.modulePkgJsonPath}@${pkgName}`);
      const dependedVersion = dependencies[pkgName];
      if (Object.prototype.hasOwnProperty.call(dependencies, pkgName)) {
        let dependency = this.collectDependency(pkgName, dependedVersion, modulePkgNode.moduleName, modulePkgNode, queue, depType);
        if (dependency) {
          modulePkgNode.children.push(dependency);
        }
      }
    }
  }

  private collectFromTree(root: ModulePkgNode, npmDeps: Dependency[], moduleDepMap: Map<string, Dependency>, filter?: DependencyFilter) {
    return this.collectDependenciesFromTree({ root, npmDeps, moduleDepMap, filter });
  }

  /**
   * 广度优先从依赖树中遍历收集依赖
   * 写过滤器时注意前后区别
   *
   * @param params
   *   root 依赖树
   *   npmDeps npm依赖数组
   *   moduleDepMap 本地模块依赖map
   *   filter 过滤器
   *   shouldCollect 是否收集进npmDeps和moduleDepMap
   *
   */
  collectDependenciesFromTree(params : {
    root: ModulePkgNode,
    npmDeps: Dependency[],
    moduleDepMap: Map<string, Dependency>,
    filter?: DependencyFilter,
    shouldCollect?: DependencyCollector
  }) {
    const { root, npmDeps, moduleDepMap, filter, shouldCollect } = params;
    const queue: ModulePkgNode[] = [...root.children];
    const seen = new Set<string>();
    const uniqueIds = new Map(npmDeps.map((npm) => [npm.getDependencyName(), npm.getDependencyRootPath()]));
    while (queue.length > 0) {
      const node = queue.shift()!;

      // 已收集节点, 用于循环依赖剪枝
      if (seen.has(node.modulePkgJsonPath)) {
        continue;
      }

      // 前置过滤器, 满足条件时, 当前node节点及它的子节点都不会被收集
      if (filter && !filter(node, true, false)) {
        continue;
      }
      const uniqueFlag = uniqueIds.get(node.npm.getDependencyName());
      // shouldCollect为空时，默认收集，不影响原有逻辑
      const collectable = !shouldCollect || shouldCollect(node);
      if (!uniqueFlag && collectable) {
        npmDeps?.push(node.npm);
        uniqueIds.set(node.npm.getDependencyName(), node.npm.getDependencyRootPath());
      }
      if (node.module && node.module.isLocal() && collectable) {
        const dependency = moduleDepMap.get(node.module.getModuleName());
        if (!dependency) {
          moduleDepMap.set(node.module.getModuleName(), node.module);
        } else {
          if (compareDependencyEnum(node.module.getDependencyEnum(), dependency.getDependencyEnum())) {
            moduleDepMap.set(node.module.getModuleName(), node.module);
          }
        }
      }

      // 后置过滤器, 满足条件时, 当前node节点仍会收集, 但它的子节点不会被收集
      if (filter && !filter(node, false, true)) {
        continue;
      }
      queue.push(...node.children);
      seen.add(node.modulePkgJsonPath);
    }
  }

  // 收集依赖阶段不再过滤hap，会直接抛出异常
  private collectDependency(
    pkgName: string,
    dependedVersion: string,
    parentPkgName: string,
    curModulePkgObj: ModulePkgNode,
    pkgQueue: ModulePkgNode[],
    dependencyEnum: DependencyEnum,
  ): ModulePkgNode | undefined {
    // 该依赖npm包的package.json路径
    const pkgFileName = this._isOhpmDependency ? CommonConst.OH_PACKAGE_JSON5 : CommonConst.PACKAGE_JSON;
    const packageResolver: BasePackageResolver = this._isOhpmDependency ? new OhpmPackageResolver() : new NpmPackageResolver();
    const devPkgJsonPath = packageResolver.resolvePackagePath(pkgName, path.dirname(curModulePkgObj.modulePkgJsonPath), log);
    const isModuleDependency = curModulePkgObj.npm.isModuleDependency();
    if (devPkgJsonPath === undefined || !fs.existsSync(devPkgJsonPath)) {
      if (fs.existsSync(path.resolve(this._model.getProjectDir(), CommonConst.OH_MODULES))
        && !this.isIndirectDevdependency(curModulePkgObj?.modulePkgJsonPath, dependencyEnum)) {
        log.warn(`The current module '${this._moduleName}' has dependency which is not installed at its ${pkgFileName}.`);
      }
      return undefined;
    }
    const store: ModulePkgNode | undefined = hvigorCore.getCache('DEP_NODE').get(`${pkgName}@${devPkgJsonPath}`);
    if (store) {
      const npmDependency = new DependencyBuilder()
        .setDependencyName(store.npm.getDependencyName())
        .setDependedVersion(dependedVersion)
        .setDependencyRootPath(store.npm.getDependencyRootPath())
        .setPkgJsonPath(store.npm.getPackageFilePath())
        .setPkgJsonObj(store.npm.getPackageJsonObj())
        .setDependencyType(store.npm.getDependencyType())
        .setDependencyEnum(dependencyEnum)
        .setModuleJsonObj(store.npm.getModuleJsonObj())
        .setIsLocal(store.npm.isLocal())
        .setLastDependencyName(parentPkgName)
        .setIsModuleDependency(isModuleDependency)
        .buildDefaultNpmDependency();
      const current: ModulePkgNode = {
        npm: npmDependency,
        parent: curModulePkgObj,
        children: [...store.children],
        moduleName: store.moduleName,
        modulePkgJsonPath: store.modulePkgJsonPath,
      };
      if (store.module) {
        current.module = new DependencyBuilder()
          .setModule(store.module.getModule())
          .setDependencyName(store.module.getDependencyName())
          .setDependedVersion(dependedVersion)
          .setDependencyRootPath(store.module.getDependencyRootPath())
          .setPkgJsonPath(store.module.getPackageJsonPath())
          .setPkgJsonObj(store.module.getPackageJsonObj())
          .setDependencyType(store.module.getDependencyType())
          .setDependencyEnum(dependencyEnum)
          .setIsLocal(true)
          .setModuleJsonObj(store.module.getModuleJsonObj())
          .setLastDependencyName(parentPkgName)
          .setIsModuleDependency(isModuleDependency)
          .buildDefaultModuleDependency();
      }
      return current;
    }

    const moduleJsonPath = () =>
      new BasePackageResolver('src/main/module.json', CommonConst.OH_MODULES).resolvePackagePath(pkgName, path.dirname(curModulePkgObj.modulePkgJsonPath), log);
    const moduleJson5Path = () =>
      new BasePackageResolver('src/main/module.json5', CommonConst.OH_MODULES).resolvePackagePath(
        pkgName,
        path.dirname(curModulePkgObj.modulePkgJsonPath),
        log,
      );

    // 读取模块信息的时候，应该使用dependencyMap里面的JSON文件而不是模块内的oh-package.json，否则没有将插件中动态修改的内容反映出来
    const cachedPkgJsonPath = ohPackageLoader.getNodeOhPackagePath(devPkgJsonPath);

    const devPkgJsonObj = this.getFinalOhPkgJsonObj(cachedPkgJsonPath);

    const dependencyType =
      this.getHarmonyDependencyType(devPkgJsonPath, devPkgJsonObj, curModulePkgObj.modulePkgJsonPath, pkgName) ??
      this.getSODependencyType(devPkgJsonObj.name) ??
      DependencyType.DEPENDENCY_TYPE_OTHER;
    switch (dependencyType) {
      case DependencyType.DEPENDENCY_TYPE_HAP: {
        log.warn(
          `HAP dependency (module "${pkgName}") detected in path "${curModulePkgObj.modulePkgJsonPath}".
          Try the following:
          1. Remove the dependency on module "${pkgName}".
          2. Change module "${pkgName}" to a HAR or HSP.`,
        );
      } // falls through 依赖类型为hap也要return
      case undefined:
        return;
      default:
        break;
    }

    const pkgJsonDir = path.dirname(devPkgJsonPath);
    this.checkHarStatusIsCompatibleForHap(pkgJsonDir, devPkgJsonObj);
    this._allDependency.add(`${devPkgJsonPath}@${pkgName}`);
    // 这里由于时序问题不能用ModuleModel来获取所有的子模块,只需要HvigorNode路径可以使用project对象获取
    const modules = hvigorCore.getProject()?.getAllSubModules();
    if (!modules) {
      return;
    }
    const moduleJsonObj = this.getModuleJsonObj(moduleJson5Path()) ?? this.getModuleJsonObj(moduleJsonPath());

    // 收集项目中所有模块的srcPath，跟pkgPath进行比对,识别出本地依赖于远程依赖，
    // 判断依赖的路径与模块的路径是否相同，硬链接方案已废弃，不再判断ino值
    let pkgJsonPath = devPkgJsonPath;
    let isLocalDep = false;
    let moduleDep: DefaultModuleDependency | undefined;

    const matchModule = modules.find((mod) => mod.getNodeDir() === pkgJsonDir);
    if (matchModule) {
      pkgJsonPath = ohPackageLoader.getNodeOhPackagePath(path.join(matchModule.getNodeDir(), pkgFileName));
      const moduleDependency = new DependencyBuilder()
        .setModule(matchModule)
        .setDependencyName(pkgName)
        .setDependedVersion(dependedVersion)
        .setLastDependencyName(curModulePkgObj.moduleName)
        .setDependencyRootPath(matchModule.getNodeDir())
        .setPkgJsonPath(pkgJsonPath)
        .setPkgJsonObj(devPkgJsonObj)
        .setDependencyType(dependencyType)
        .setDependencyEnum(dependencyEnum)
        .setIsLocal(true)
        .setModuleJsonObj(moduleJsonObj)
        .setIsModuleDependency(isModuleDependency)
        .buildDefaultModuleDependency();
      isLocalDep = true;
      moduleDep = moduleDependency;
    }

    const originPkgJsonPath = ohPackageLoader.getOriginPathFromUpdatePath([pkgJsonPath])[0];

    const defaultNpmDependency = new DependencyBuilder()
      .setDependencyName(pkgName)
      .setDependedVersion(dependedVersion)
      .setDependencyRootPath(originPkgJsonPath)
      .setPkgJsonPath(pkgJsonPath)
      .setPkgJsonObj(devPkgJsonObj)
      .setDependencyType(dependencyType)
      .setDependencyEnum(dependencyEnum)
      .setModuleJsonObj(moduleJsonObj)
      .setIsLocal(isLocalDep)
      .setIsModuleDependency(isModuleDependency)
      .buildDefaultNpmDependency();
    const moduleObj: ModulePkgNode = {
      moduleName: moduleDep?.getModuleName() ?? '',
      modulePkgJsonPath: devPkgJsonPath,
      parent: curModulePkgObj,
      children: [],
      npm: moduleDep ?? defaultNpmDependency,
      module: moduleDep,
    };
    if (
      dependencyType === DependencyType.DEPENDENCY_TYPE_HAR ||
      dependencyType === DependencyType.DEPENDENCY_TYPE_OTHER ||
      dependencyType === DependencyType.DEPENDENCY_TYPE_HSP
    ) {
      pkgQueue.push(moduleObj);
    }
    return moduleObj;
  }

  private getModuleJsonObj(moduleJsonPath?: string) {
    if (moduleJsonPath === undefined || !fs.existsSync(moduleJsonPath)) {
      return;
    }
    return getJson5Obj(moduleJsonPath);
  }

  /**
   * 通过config.json/module.json5判断当前模块是否为鸿蒙依赖（har/hsp）
   *
   * @param {string} pkgPath 当前模块的路径
   * @returns {boolean}
   * @private
   */
  private getDependencyTypeByProfile(pkgPath: string): DependencyType | undefined {
    const module5File: string = path.resolve(pkgPath, 'src', 'main', CommonConst.MODULE_JSON5);
    const moduleFile: string = path.resolve(pkgPath, 'src', 'main', CommonConst.MODULE_JSON);
    const configFile: string = path.resolve(pkgPath, 'src', 'main', CommonConst.CONFIG_JSON);

    return (
      this.getDependencyTypeByProfileType(module5File) || this.getDependencyTypeByProfileType(moduleFile) || this.getDependencyTypeByProfileType(configFile)
    );
  }

  /**
   * 获取当前模块的moduleType
   *
   * @param {string} runtimeJson config.json/module.json5
   * @returns {boolean}
   * @private
   */
  private getDependencyTypeByProfileType(runtimeJson: string): DependencyType | undefined {
    if (!fs.existsSync(runtimeJson)) {
      return undefined;
    }

    const profileJson: ModuleOptObj | ConfigOptObj = getJson5Obj(runtimeJson);

    // hsp只支持stage模型
    const isHsp = (profileJson as ModuleOptObj).module?.type === ModuleType.Shared;
    const isHar = (profileJson as ModuleOptObj).module?.type === ModuleType.Har || (profileJson as ConfigOptObj).module?.distro?.moduleType === ModuleType.Har;
    const isHap =
      (profileJson as ModuleOptObj).module?.type === ModuleType.Entry ||
      (profileJson as ModuleOptObj).module?.type === ModuleType.Feature ||
      (profileJson as ConfigOptObj).module?.distro?.moduleType === ModuleType.Entry ||
      (profileJson as ConfigOptObj).module?.distro?.moduleType === ModuleType.Feature;

    if (isHsp) {
      return DependencyType.DEPENDENCY_TYPE_HSP;
    }
    if (isHar) {
      return DependencyType.DEPENDENCY_TYPE_HAR;
    }
    if (isHap) {
      return DependencyType.DEPENDENCY_TYPE_HAP;
    }
    return undefined;
  }

  /**
   * 通过config.json/module.json5判断当前模块是否支持stage
   *
   * @param {string} pkgPath 当前模块的路径
   * @returns {boolean}
   * @private
   */
  private checkHspOrHarStatusByProfile(pkgPath: string): boolean {
    const module5File: string = path.resolve(pkgPath, 'src', 'main', CommonConst.MODULE_JSON5);
    const moduleFile: string = path.resolve(pkgPath, 'src', 'main', CommonConst.MODULE_JSON);
    const configFile: string = path.resolve(pkgPath, 'src', 'main', CommonConst.CONFIG_JSON);
    return this.checkHspOrHarStatus(module5File, false) || this.checkHspOrHarStatus(moduleFile, false) || this.checkHspOrHarStatus(configFile, true);
  }

  /**
   * 检查当前模块的model与hap是否一致
   * @param runtimeJson
   * @param depModuleIsFAMode 依赖模块是否为FA专用
   * @private
   */
  private checkHspOrHarStatus(runtimeJson: string, depModuleIsFAMode: boolean): boolean {
    if (!fs.existsSync(runtimeJson)) {
      return false;
    }

    const profileJson: ModuleOptObj | ConfigOptObj = getJson5Obj(runtimeJson);

    // hsp只支持stage模型
    const isHsp = (profileJson as ModuleOptObj).module?.type === ModuleType.Shared;
    const isHar = (profileJson as ModuleOptObj).module?.type === ModuleType.Har || (profileJson as ConfigOptObj).module?.distro?.moduleType === ModuleType.Har;

    // 发现har或hsp包的模型与hap不同
    if ((isHar || isHsp) && depModuleIsFAMode) {
      log.printErrorExit(
        'MODEL_NOT_CONSISTENT',
        [this._moduleName],
        [[solution, runtimeJson]]
      )
    }
    return true;
  }

  /**
   * 1. 根据config.json/module.json5判断是不是鸿蒙依赖（har/hsp）
   * 2. 根据包管理的关键特征判断是否是har报
   * 3. 考虑循环依赖，用set过滤重复项，向queue中添加package.json路径
   *
   * @param {string} pkgJsonPath 当前模块依赖的npm的package.json/oh-package.json5路径
   * @param {OhosPackageJson} devPkgJsonObj 当前模块依赖的package.json对象
   * @param curPkgJsonPath
   * @param pkgName
   * @returns {boolean}
   * @private
   */
  private getHarmonyDependencyType(pkgJsonPath: string, devPkgJsonObj: OhosPackageJson, curPkgJsonPath: string, pkgName: string): DependencyType | undefined {
    // package.json/oh-package.json5不存在时,说明不是har包
    if (!pkgJsonPath || devPkgJsonObj === null) {
      return undefined;
    }

    const pkgPath = path.dirname(pkgJsonPath);
    const originPkgJsonDir = ohPackageLoader.getOriginPathFromUpdatePath([pkgJsonPath])?.[0] ?? pkgPath;
    const isHarPack = this.isHarForDiffPackManagement(pkgPath, devPkgJsonObj);

    if (this._isFaMode) {
      this.legacyDependencyManagerHelper.checkHspOrHarStatusForFAByProfile(pkgPath, devPkgJsonObj);
    } else {
      this.checkHspOrHarStatusByProfile(pkgPath);
    }

    // 模块类型只能从包的原本路径下的module.json5下判断，更新后的dependencyMap里不存在module.json5，所以需要改成原本的PkgJson目录
    const dependencyTypeByProfile = this.getDependencyTypeByProfile(originPkgJsonDir);

    if (
      isHarPack &&
      this._allDependency.has(`${pkgJsonPath}@${pkgName}`) &&
      pkgJsonPath === curPkgJsonPath &&
      dependencyTypeByProfile === DependencyType.DEPENDENCY_TYPE_HSP
    ) {
      log.printErrorExit(
        'HSP_RELY_HSP',
        [this._moduleName, pkgPath],
      )
    }
    if (!isHarPack || !devPkgJsonObj.name) {
      return undefined;
    }
    return dependencyTypeByProfile;
  }

  /**
   * npm和ohpm的不同包管理机制下,判断一个依赖是否为har包
   * 1.ohpm只需要判断依赖中是否包含oh-package.json5
   * 2.npm需要判断package.json5中是否包含ohos字段
   *
   * @param {string} pkgPath
   * @param {OhosPackageJson} devPkgJsonPathObj
   * @returns {boolean}
   * @private
   */
  private isHarForDiffPackManagement(pkgPath: string, devPkgJsonPathObj: OhosPackageJson): boolean {
    if (this._isOhpmDependency) {
      return fs.existsSync(pkgPath);
    } else {
      return fs.existsSync(pkgPath) && devPkgJsonPathObj.ohos !== undefined;
    }
  }

  /**
   * 针对hap依赖不同har的一些业务规则校验
   * 1.FA模型不支持依赖闭源har(stage模型+esModule编译)
   *
   * @param {string} pkgPath
   * @param {OhosPackageJson} devPkgJsonPathObj
   * @private
   */
  private checkHarStatusIsCompatibleForHap(pkgPath: string, devPkgJsonPathObj: OhosPackageJson) {
    // project和har收集依赖时不需要校验,不需要关注依赖的har的形态
    if (this._model instanceof CoreProjectModelImpl) {
      return;
    }
    const moduleModel = this._model as CoreModuleModelImpl;
    if (moduleModel.isHarModule()) {
      return;
    }

    const harArtifactType = this._isOhpmDependency ? devPkgJsonPathObj.artifactType : devPkgJsonPathObj.ohos?.artifactType;

    if (harArtifactType === undefined || harArtifactType === ArtifactType.ORIGINAL) {
      // 开源har没有限制
      return;
    }

    if (moduleModel instanceof LegacyModuleModelImpl) {
      log.printErrorExit(
        'FA_RELY_OH_NPM',
        [this._moduleName, pkgPath],
        [[solution]]
      )
    }
  }

  /**
   * 通过入口文件路径地址判断当前依赖是否为本地依赖 暂时用于判断so类型依赖
   * 若包含oh_modules字段则为远程依赖
   * @param path 依赖入口文件路径地址
   */
  static isLocalDependencyForSo(path: string): boolean {
    return !path.includes('oh_modules');
  }

  /**
   * 判断指定依赖是否为本地依赖
   * @param dependency
   * @param moduleModelNodePaths
   */
  static isLocalDependency(dependency: Dependency, moduleModelNodePaths: Set<string>): boolean {
    return moduleModelNodePaths.has(dependency.getDependencyRootPath());
  }

  /**
   * 通过oh-package.json5获取moduleName
   * @param dependencyRootPath
   * @private
   */
  static getModuleNameFromOhPkgJson(dependencyRootPath: string): string {
    const ohPkgJsonPath = ohPackageLoader.getNodeOhPackagePath(path.resolve(dependencyRootPath, CommonConst.OH_PACKAGE_JSON5));
    if (!fs.existsSync(ohPkgJsonPath)) {
      return '';
    }

    const ohPkgJsonObj: OhPackageJsonOpt = getJson5Obj(ohPkgJsonPath);
    return ohPkgJsonObj?.name ?? '';
  }

  /**
   * 获取远程依赖routerMap配置集合
   * @param remoteDependency
   * @private
   */
  static getRemoteDependencyRouterMapObjList(remoteDependency: Dependency): RouterMapObj[] | undefined {
    const remoteDependencySrcMainPath = path.resolve(remoteDependency.getDependencyRootPath(), BuildDirConst.SRC, BuildDirConst.MAIN);
    const moduleJsonObj: ModuleOptObj = this.getModuleObjByRemoteDependency(remoteDependency);
    const routerMapFileName = parsingProfileName(moduleJsonObj?.module.routerMap) ?? undefined;
    if (!routerMapFileName) {
      return;
    }

    const routerMapJsonPath = path.resolve(remoteDependencySrcMainPath, BuildDirConst.RESOURCES_BASE_PROFILE_DIR, `${routerMapFileName}.json`);
    const routerMapOpt: RouterMapOptions.RouterMapOpt = getJson5Obj(routerMapJsonPath);

    routerMapOpt.routerMap.forEach((remoteRouterMapObj) => {
      Object.assign(remoteRouterMapObj, { moduleNodeDir: remoteDependency.getDependencyRootPath() });
    });
    return routerMapOpt.routerMap ?? undefined;
  }

  /**
   * 获取本地依赖routerMap配置集合
   * @param localDependency
   * @param projectModel
   * @param targetName
   */
  static getLocalDependencyRouterMapObjList(localDependency: Dependency | undefined, projectModel: ProjectModel, targetName = DefaultTargetConst.DEFAULT_TARGET): RouterMapObj[] | undefined {
    if (!localDependency) {
      return;
    }

    const moduleModelByDependency = this.getModuleModelByDependency(localDependency, projectModel);
    if (!moduleModelByDependency) {
      return;
    }

    const dependencyModuleRouterMapJsonPath = this.getDependencyModuleRouterMapJsonPath(moduleModelByDependency, targetName);
    if (!dependencyModuleRouterMapJsonPath || !fs.existsSync(dependencyModuleRouterMapJsonPath)) {
      return undefined;
    }

    const routerMapOpt: RouterMapOptions.RouterMapOpt = getJson5Obj(dependencyModuleRouterMapJsonPath);
    routerMapOpt.routerMap.forEach((localRouterMapObj) => {
      Object.assign(localRouterMapObj, { moduleNodeDir: moduleModelByDependency.getModule().getNodeDir() });
    });
    return routerMapOpt.routerMap ?? undefined;
  }

  /**
   * 获取依赖模块的routerMap配置路径
   * @private
   * @param dependencyModuleModel
   * @param targetName
   */
  static getDependencyModuleRouterMapJsonPath(dependencyModuleModel: ModuleModel | undefined, targetName = DefaultTargetConst.DEFAULT_TARGET): string | undefined {
    if (!dependencyModuleModel) {
      return undefined;
    }

    const routerMapFileName = this.getRouterMapJsonFileName(dependencyModuleModel);
    if (!routerMapFileName) {
      return undefined;
    }

    const dirs: string[] = [];
    const modulePath = dependencyModuleModel.getModule().getNodeDir();
    const opts = dependencyModuleModel.getProfileOpt();

    if (opts?.targets?.length) {
      const harTarget = opts.targets.find((harTargetName) => harTargetName.name === targetName);
      dirs.push(
        ...FileUtil.convertToAbsolutePaths(harTarget?.resource?.directories || [], modulePath)
      );
    }

    for (const dir of dirs) {
      const jsonPath = path.resolve(dir, 'base', 'profile', `${routerMapFileName}.json`);
      if (fs.existsSync(jsonPath)) {
        return jsonPath;
      }
    }
    // fallback dir
    return path.resolve(
      dependencyModuleModel.getModule().getNodeDir(),
      BuildDirConst.SRC,
      BuildDirConst.MAIN,
      BuildDirConst.RESOURCES_BASE_PROFILE_DIR,
      `${routerMapFileName}.json`
    );
  }

  /**
   * 获取模块配置的路由表文件名
   * @param moduleModel
   */
  static getRouterMapJsonFileName(moduleModel: ModuleModel | undefined): string | undefined {
    if (!moduleModel) {
      return undefined;
    }

    const moduleJsonPath = path.resolve(moduleModel.getModule().getNodeDir(), BuildDirConst.SRC, BuildDirConst.MAIN, CommonConst.MODULE_JSON5);
    const moduleJsonObj: ModuleOptObj = resModelLoader.getModuleJson(moduleJsonPath);
    const routerMap = moduleJsonObj.module.routerMap;
    return parsingProfileName(routerMap);
  }

  /**
   * 通过本地依赖获取对应moduleModel
   * @param dependency
   * @param projectModel
   */
  static getModuleModelByDependency(dependency: Dependency, projectModel: ProjectModel): ModuleModel | undefined {
    if (!dependency.isLocal()) {
      return;
    }

    return projectModel.getAllModules().find((eachModuleModel) => eachModuleModel.getProjectDir() === dependency.getDependencyRootPath());
  }

  /**
   * 获取远程依赖module.json对象
   *
   * @param remoteDependency
   * @private
   */
  static getModuleObjByRemoteDependency(remoteDependency: Dependency): ModuleOptObj {
    const remoteDependencySrcMainPath = path.resolve(remoteDependency.getDependencyRootPath(), BuildDirConst.SRC, BuildDirConst.MAIN);
    const remoteModuleJsonPath = path.resolve(remoteDependencySrcMainPath, CommonConst.MODULE_JSON);
    const remoteModuleJson5Path = path.resolve(remoteDependencySrcMainPath, CommonConst.MODULE_JSON5);

    // 针对存在module.json5的特殊远程依赖场景:若存在module.json即读取 不存在则读取module.json5
    const realRemoteModuleJsonPath = fs.existsSync(remoteModuleJsonPath)
      ? remoteModuleJsonPath
      : fs.existsSync(remoteModuleJson5Path)
        ? remoteModuleJson5Path
        : '';
    return getJson5Obj(realRemoteModuleJsonPath);
  }

  private getSODependencyType(pkgName: string | undefined) {
    return pkgName?.includes('.so') ? DependencyType.DEPENDENCY_TYPE_SO : undefined;
  }

  /**
   * 判断是否为间接依赖的devDependencies
   *
   * @param modulePkgJsonPath  父节点的oh_package.json5路径
   * @param dependencyEnum  当前节点的类型
   */
  private isIndirectDevdependency(modulePkgJsonPath: string | undefined,dependencyEnum: DependencyEnum) : boolean{
    // 如果父节点不存在，则不该存在该校验，应该报错
    if(!modulePkgJsonPath) {
      return false;
    }

    // 如果父节点路径包含oh_modules证明父节点为远程依赖的节点
    // 如果此时当前节点类型为DevDependencies则当前节点为间接依赖的devDependencies
    return toUnixPath(modulePkgJsonPath).includes('/oh_modules/') && dependencyEnum === DependencyEnum.DEV_DEPENDENCIES
  }

  /**
   * 获取最终的ohPkgJson的对象ohPkgJsonObj
   * 判断是否需要用overrideDependencyMap来替换ohPkgJsonObj的依赖值
   *
   * @param modulePath
   * @private
   */
  private getFinalOhPkgJsonObj(modulePath: string): any {
    let ohPkgJsonObj: PackageJson = getJson5Obj(modulePath);
    if (ohPkgJsonObj && ohPkgJsonObj.name && ohModulesLockLoader.isNeedOverrideWithLockJson(ohPkgJsonObj.name)) {
      ohPkgJsonObj = ohModulesLockLoader.getOverrideDependencyPkgObj(ohPkgJsonObj.name, ohPkgJsonObj);
    }
    return ohPkgJsonObj;
  }
}

/**
 * 获取依赖入口文件
 * @param realDependencyPath
 * @param dynamicImportOhPkgObj
 * @private
 */
export function getEntryMainFilePath(realDependencyPath: string, dynamicImportOhPkgObj: OhPackageJsonOpt): string {
  const nodeEntryFile = path.resolve(realDependencyPath, findNodeEntryFiles(dynamicImportOhPkgObj)[0]);
  if (fs.existsSync(nodeEntryFile)) {
    return nodeEntryFile;
  } else {
    // 如果入口文件不存在则需要解析oh-package.json5并读取main/types/index.js
    const entryPath = getEntryPath(dynamicImportOhPkgObj.main, dynamicImportOhPkgObj.types);
    const primaryEntryFile = path.resolve(realDependencyPath, entryPath);
    return fs.existsSync(primaryEntryFile) ? primaryEntryFile : '';
  }
}

export interface ModulePkgNode {
  moduleName: string;
  modulePkgJsonPath: string;
  parent?: ModulePkgNode;
  children: ModulePkgNode[];
  npm: Dependency;
  module?: DefaultModuleDependency;
}

export function getEntryPath(main: string, types: string): string {
  if (main && main !== '') {
    return main;
  }
  if (types && types !== '') {
    return types;
  }
  return BuildArtifactConst.INDEX_ETS;
}

